#!/usr/bin/env python2
#-*- coding: utf-8 -*-

import functools

from Ump import utils
from Ump.umptypes import UmpPath
from Ump.common import exception
from Ump.common import config
from Ump.common import log
from Ump.common.utils import inspect_func
import math
from Ump.objs.manager_base import Manager
from Ump.objs.session_wrapper import enable_log_and_session, _sw
from Ump.objs.db import models

from Ump.lich.pool import LichPool, LichPoolParam

LOG = log.get_log('Ump.objs.pool.manager')


def check_pool(exists=True):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(self, params):
            pool = self.get_pool(params)
            if exists and not pool:
                raise exception.PoolNotFound(params)
            elif not exists and pool:
                raise exception.PoolFound(params)
            return func(self, params)
        return wrapper
    return decorator


@models.add_model(models.Pool)
class PoolManager(Manager):
    '''
    '''

    def __init__(self):
        super(PoolManager, self).__init__()
        self.type = 'pool'
        self.lichPool = LichPool()
        
    @enable_log_and_session(resource='pool', event='create')
    def create(self, _logger, kwargs):
        path = kwargs['path']
        if kwargs.get('ec_data') and kwargs.get('ec_code'):
            ec_data = kwargs['ec_data']
            ec_code = kwargs['ec_code']
            kwargs['repnum'] = None
            #if int(ec_data) < int(ec_code):
            #    raise exception.ParameterWrong('make sure ec_data big ec_code')
            host_num = len(_sw.db_hosts())
            ec_num = int(ec_code) + int(ec_data)
            if ec_num > 6:
                raise exception.ParameterWrong('k+r的值必须小于等于6')
            if host_num <= (ec_num + int(ec_data)):
                raise exception.ParameterWrong("请确保集群中主机数大于等于k+r+r")

        else:
            ec_code = None
            ec_data = None


        _logger.update_props(oplog_obj=path)

        pool = self._create(**kwargs)
        return pool

    def _create(self, path=None,  ec_data=None, ec_code=None, repnum=None, quota=None,
                protection_domain_ids=None, priority=-1, op_username=None, chap_name=None,
                chap_password=None, cluster_id=None, provisioning='thin', **kw):
        cluster = _sw.get_cluster(cluster_id=cluster_id)
        pool = _sw.get_pool(path)
        if pool:
            raise exception.AlreadyExists(path=path)

        pds = _sw.get_list_by_ids(models.ProtectionDomain, protection_domain_ids)
        user = models.User.query.filter_by(name=op_username).first()
        self.check_quota(quota, user)
        values = {
            'name' : path.pool_name,
            'cluster_id': cluster.id,
            'protection_domains': pds,
            'protocol': path.protocol,
            'user_id': _sw.get_user_id(path.username),
            'repnum': repnum if repnum else None,
            'ec_data' : ec_data,
            'ec_code' : ec_code,
            'quota': quota,
            'chap_name': chap_name,
            'chap_password': chap_password,
            'priority': priority,
            'provisioning': provisioning,
        }

        param = LichPoolParam(path, ec_data, ec_code, repnum, cluster_id=cluster_id, priority=priority)
        self.lichPool.create(param)

        pool = models.Pool(values).save()

        if path.protocol == 'iscsi':
            self.lichPool.set_attr(param, 'lich_system_username', pool.sp_chap_name)
            self.lichPool.set_attr(param, 'lich_system_password', pool.sp_chap_password)
        return pool

    def check_quota(self, quota, user, skip_pool_id=-1, id=None):
        '''create and update pool check quota if over user'quota 
        '''
        if not quota:
            return 
        quota = int(quota)
        pools_quota = 0
        volumes_quota = 0
        free = quota * 2
        if user.sp_quota:
            if quota > user.sp_quota:
                raise exception.PoolQuotaError("池的配额不能超过用户配额")
            if user.pools:
                for pool in user.pools:
                     if int(pool.id) == int(skip_pool_id):
                         continue
                     if pool.quota:
                         pools_quota += int(pool.quota)
                     else:
                         for p_v in pool.volumes:
                             volumes_quota += int(p_v.size_gb)
                     free = user.sp_quota - pools_quota - volumes_quota
        if id:
            pool = models.Pool.query.filter_by(id=id).first()
            use_quota = pool.use_quota
            if quota < use_quota:
                raise  exception.QuotaError("请确保输入的配额大于等于该池下卷的容量")
            if pool.quota:
                free = free + int(pool.quota)
                if quota > free:
                    raise exception.PoolQuotaError("池的配额已经超出剩余容量和自身原有的容量总和")
        if quota > free:
            raise exception.QuotaError(free=free)

    @enable_log_and_session(resource='pool', event='delete')
    def delete(self, _logger, kwargs):
        pool = _sw.get_pool(kwargs)
        if not pool:
            raise exception.PoolNotFound(**kwargs)

        _logger.update_props(oplog_obj=pool.path)

        if pool.volumes:
            for vol in pool.volumes:
                LOG.info('--- vol %s' % vol)
            raise exception.PoolNotEmpty(path=pool.path)

        path = pool.path
        cluster_id = pool.cluster_id


        param = LichPoolParam(path, cluster_id=cluster_id)
        self.lichPool.delete(param)

        pool.delete()

        return True

    @enable_log_and_session(resource='pool', event='update')
    def update(self, _logger, kwargs):
        pool = _sw.get_pool(kwargs)
        if not pool:
            raise exception.PoolNotFound(kwargs)

        _logger.update_props(oplog_obj=pool.path)

        return self._update(pool, **kwargs)

    def _update(self, pool, quota=None, id=None, repnum=None, priority=None, ec_data=None, ec_code=None,
                    protection_domain_ids=None, chap_name=None, chap_password=None, op_username=None,  provisioning=None, **kw):
        values = {}
        pool_volnum_size = models.Volume.query.filter_by(pool_id=id)
        user = models.User.query.filter_by(name=op_username).first()
        self.check_quota(quota, user, id=pool.id)
        # size = 0
        # for v_size in pool_volnum_size:
        #     tmp_size = self.deal_quota(v_size.size)
        #     size += tmp_size
        # if int(quota) < size:
        #     raise exception.InvalidParameter('quota must big than %d' % size)

        utils.update_values(values, 'repnum', repnum)
        utils.update_values(values, 'ec_data', ec_data)
        utils.update_values(values, 'ec_code', ec_code)
        utils.update_values(values, 'quota', quota)
        utils.update_values(values, 'chap_name', chap_name)
        utils.update_values(values, 'chap_password', chap_password)
        utils.update_values(values, 'provisioning', provisioning)

        if priority:
            if priority == '-1':
                pool = self._priority(pool, 'default')
            else:
                pool = self._priority(pool, priority)
            utils.update_values(values, 'priority', priority)

        param = LichPoolParam(pool.path, cluster_id=pool.cluster_id, priority=priority)
        if protection_domain_ids is not None:
            hds = _sw.get_list_by_ids(models.ProtectionDomain, protection_domain_ids)
            utils.update_values(values, 'protection_domains', hds, is_list=True)
        if values.get('ec_data') and values.get('ec_code'):
            values['repnum'] = None
        pool.update(values)

        self.lichPool.set_attr(param, 'lich_system_username', pool.sp_chap_name)
        self.lichPool.set_attr(param, 'lich_system_password', pool.sp_chap_password)

        return pool

    def deal_quota(self, value):
        return value/(10**9)
    def pool_sync(self, cluster_id, is_create=False):
        protocols = config.protocols
        for protocol in protocols:
            self._pool_sync(cluster_id, protocol, is_create=is_create)

    def _pool_sync(self, cluster_id, protocol, is_create=False):
        param = LichPoolParam(UmpPath('oops', protocol=protocol), cluster_id=cluster_id)
        res = self.lichPool.list(param)

        pools = res.split('\n')
        pools = [pool.strip().split()[-1] for pool in pools if pool and pool.strip().split()]

        pools_in_db = _sw.db_pools({'cluster_id': cluster_id, 'protocol': protocol})
        #for pool in pools_in_db:
        #    if pool.realname not in pools :
        #        pool.delete()

        for poolname in pools:
            values = {
                'cluster_id' : cluster_id,
                'protocol': protocol,
                'name': poolname,
            }

            owner_values = self._get_pool_owner_kv(poolname)
            values.update(owner_values)
             
            name = values.get('name')
            poolsql = _sw.db_pools({'cluster_id': cluster_id, 'protocol': protocol, 'name': name})
            if poolsql:
                poolsql[0].update(values)
            else:
                if is_create:
                    models.Pool(values).save()

    def _get_pool_owner_kv(self, name):
        try:
            name_components = name.split(':')
            poolname = ":".join(name_components[1:])
            user = _sw.get_user(username=name_components[0])
            user_id = user.id
        except Exception, e:
            poolname = name
            user_id = 1

        values = {
            'name': poolname,
            'user_id': user_id,
        }
        return values

    @enable_log_and_session(resource='pool', event='priority')
    def priority(self, _logger, kwargs):
        priority = kwargs['priority']

        pool = _sw.get_pool(kwargs)
        if not pool:
            raise exception.PoolNotFound(kwargs)

        _logger.update_props(oplog_obj=pool.path)

        return self._priority(pool, priority)

    def _priority(self, pool, priority):

        ## DB
        pool.priority = priority

        # LICH
        param = LichPoolParam(pool.path, priority=priority)
        self.lichPool.set_priority(param)

        return pool


if __name__ == '__main__':
    tgt = PoolManager()
    tgt.pool_sync(1)
